package cc.mrbird.febs.common.datasource.starter.tenant;

import com.xf.common.core.entity.YesOrNoEnum;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.util.function.Supplier;

@Slf4j
@UtilityClass
public class TenantBroker {

	@FunctionalInterface
	public interface RunAs<T> {

		/**
		 * 执行业务逻辑
		 * @param tenantId
		 * @throws Exception
		 */
		void run(T tenantId) throws Exception;

	}

	@FunctionalInterface
	public interface ApplyAs<T, R> {

		/**
		 * 执行业务逻辑,返回一个值
		 * @param tenantId
		 * @return
		 * @throws Exception
		 */
		R apply(T tenantId) throws Exception;

	}

	@FunctionalInterface
	public interface NoRun {

		/**
		 * 执行业务逻辑
		 * @throws Exception
		 */
		void run() throws Exception;

	}

	@FunctionalInterface
	public interface NoApplyAs<R> {

		/**
		 * 执行业务逻辑,返回一个值
		 * @return
		 * @throws Exception
		 */
		R apply() throws Exception;

	}

	/**
	 * 以某个租户的身份运行
	 * @param tenantId 租户ID
	 * @param func
	 */
	public void runAs(String tenantId, RunAs<String> func) {
		final TenantContextHolder.TenantConfig tenantConfig = TenantContextHolder.getTenantConfig();

		try {
			if (tenantId.equals(YesOrNoEnum.No.getValue())) {
				tenantId = null;
			}
			//log.debug("TenantBroker 切换租户{} -> {}", tenantConfig.getTenantId(), tenantId);
			TenantContextHolder.setTenantConfig(tenantId, true);
			func.run(tenantId);
		}
		catch (Exception e) {
			log.debug(e.getMessage(), e);
			throw new TenantBrokerExceptionWrapper(e.getMessage(), e);
		}
		finally {
			//log.debug("TenantBroker 还原租户{} <- {}", tenantConfig, tenantId);
			TenantContextHolder.setTenantConfig(tenantConfig.getTenantId(), tenantConfig.getTenantFlag());
		}
	}

	/**
	 * 以某个租户的身份运行
	 * @param tenantId 租户ID
	 * @param func
	 * @param <T> 返回数据类型
	 * @return
	 */
	public <T> T applyAs(String tenantId, ApplyAs<String, T> func) {
		final TenantContextHolder.TenantConfig tenantConfig = TenantContextHolder.getTenantConfig();
		try {
			if (tenantId.equals(YesOrNoEnum.No.getValue())) {
				tenantId = null;
			}
			log.debug("TenantBroker 切换租户{} -> {}", tenantConfig.getTenantId(), tenantId);
			TenantContextHolder.setTenantConfig(tenantId, true);
			return func.apply(tenantId);
		}
		catch (Exception e) {
			log.debug(e.getMessage(), e);
			throw new TenantBrokerExceptionWrapper(e.getMessage(), e);
		}
		finally {
			log.debug("TenantBroker 还原租户{} <- {}", tenantConfig.getTenantId(), tenantId);
			TenantContextHolder.setTenantConfig(tenantConfig.getTenantId(), tenantConfig.getTenantFlag());
		}
	}

	/**
	 * 以某个租户的身份运行
	 * @param supplier
	 * @param func
	 */
	public void runAs(Supplier<String> supplier, RunAs<String> func) {
		runAs(supplier.get(), func);
	}

	/**
	 * 以某个租户的身份运行
	 * @param supplier
	 * @param func
	 * @param <T> 返回数据类型
	 * @return
	 */
	public <T> T applyAs(Supplier<String> supplier, ApplyAs<String, T> func) {
		return applyAs(supplier.get(), func);
	}

	/**
	 * 不以某个租户的身份运行
	 * @param noRun
	 */
	public void noRunAs(NoRun noRun) {
		final TenantContextHolder.TenantConfig tenantConfig = TenantContextHolder.getTenantConfig();
		try {
			log.debug("TenantBroker 不以租户的身份运行");
			TenantContextHolder.setTenantConfig(null, false);
			noRun.run();
		}
		catch (Exception e) {
			log.debug(e.getMessage(), e);
			throw new TenantBrokerExceptionWrapper(e.getMessage(), e);
		}
		finally {
			if (tenantConfig != null) {
				log.debug("TenantBroker 还原租户{} <- {}", tenantConfig.getTenantId(), tenantConfig.getTenantId());
				TenantContextHolder.setTenantConfig(tenantConfig.getTenantId(), tenantConfig.getTenantFlag());
			}
		}
	}

	/**
	 * 不以租户的身份运行
	 * @param noApplyAs
	 * @param <T> 返回数据类型
	 * @return
	 */
	public <T> T noApplyAs(NoApplyAs<T> noApplyAs) {
		final TenantContextHolder.TenantConfig tenantConfig = TenantContextHolder.getTenantConfig();
		try {
			log.debug("TenantBroker 不以租户的身份运行");
			TenantContextHolder.setTenantConfig(null, false);
			return noApplyAs.apply();
		}
		catch (Exception e) {
			log.debug(e.getMessage(), e);
			throw new TenantBrokerExceptionWrapper(e.getMessage(), e);
		}
		finally {
			if (tenantConfig != null) {
				log.debug("TenantBroker 还原租户{} <- {}", tenantConfig.getTenantId(), tenantConfig.getTenantId());
				TenantContextHolder.setTenantConfig(tenantConfig.getTenantId(), tenantConfig.getTenantFlag());
			}
		}
	}

	public static class TenantBrokerExceptionWrapper extends RuntimeException {

		public TenantBrokerExceptionWrapper(String message, Throwable cause) {
			super(message, cause);
		}

		public TenantBrokerExceptionWrapper(Throwable cause) {
			super(cause);
		}

	}

}
